const { fork } = require('child_process');
const zmq = require('zeromq')
    , sock = zmq.socket('sub'),
    cfg = require('../config.json'),
    pg = require('../db/db'),
    Client = require('bitcoin-core'),
    logger = require('./logger').logger;
let client;

try {
    sock.connect('tcp://' + cfg.zmq.ip + ':' + cfg.zmq.port);//'tcp://192.168.2.154:28332'
    sock.subscribe('hash');
    sock.subscribe('raw');
    logger.info(`Subscriber connected to port ${cfg.zmq.port}`);
    sock.monitor()

    //reconnect
    sock.on('disconnect', () => {
        sock.connect('tcp://' + cfg.zmq.ip + ':' + cfg.zmq.port);//'tcp://192.168.2.154:28332'
        logger.error('disconnect :tcp://' + cfg.zmq.ip + ':' + cfg.zmq.port)
    })
    //listen
    sock.on('message', async (topic, message) => {
        // logger.info('received a message related to:', topics.toString("utf8"), 'containing message:', message.toString('hex'));
        if (client == undefined) {
            initClient();
        }

        const topics = topic.toString();

        if (topics == 'rawtx') {
            // const rawTX = message.toString('hex');
            // logger.info('111111', topics);
        } else if (topics == 'rawblock') {
            // const rawBlock = message.toString('hex');
            // logger.info('22222', topics, rawBlock);
        } else if (topics == 'hashblock') {
            const rawBlock = message.toString('hex');
            logger.info('current', topics, rawBlock);
            // write DB 
            try {
                let blockInfo = await client.getBlock(rawBlock);
                await insertBlock(blockInfo);
                // fork another process
                const process = fork('services/child.js');
                //send block info to child process
                process.send({ blockInfo });
                //receive message from forked process 
                process.on('message', (message) => {
                    logger.info(`child process handle result: ${message.counter}`);
                    process.kill();
                    logger.info(`child process PID ${process.pid} is killed`);
                });
            } catch (error) {
                global.gc();
                logger.error(" error===>", error);
                initClient();
            }

        } else if (topics == 'hashtx') {
            // const txid = message.toString('hex');
            // logger.info('44444', topics, txid);
        }
    });
} catch (error) {
    logger.error(`main process error : ${error}`);
    initClient();
}

// init client 
let initClient = async () => {
    try {
        client = new Client(cfg.bitCfg);
        if (typeof client == 'undefined') {
            client = new Client(cfg.bitCfg);
        }
        logger.info('bitcoin client init success.');
    } catch (error) {
        logger.error("client init error===>", error);
    }
}

// writer block to db
async function insertBlock(blockInfo) {
    let hash = await pg.any(`SELECT hash FROM bitblock WHERE bitblock."hash" = '${blockInfo.hash}'`);
    if (hash.length == 0) {
        await pg.none(wirteBlocks(blockInfo));
        logger.info(`the current block : ${blockInfo.height} tx size : ${blockInfo.tx.length} write database success.`);
        global.gc();
    } else {
        logger.info(`the current block: ${blockInfo.height} blockHash: ${(hash[hash.length - 1]).hash} tx_size: ${blockInfo.tx.length} is already exists in database.`);
    }
}

let wirteBlocks = (result) => {

    let sql = `INSERT INTO public.bitblock( hash, confirmations, strippedsize, weight, height, version, "versionHex", merkleroot, tx, "time", 
    mediantime, nonce, bits, difficulty, chainwork, previousblockhash, nextblockhash)
	VALUES ('${result.hash}', ${result.confirmations}, ${result.strippedsize}, ${result.weight}, ${result.height}, ${result.version}, '${result.versionHex}', '${result.merkleroot}',
			'${JSON.stringify(result.tx)}',${result.time},
			'${result.mediantime}', '${result.nonce}', '${result.bits}', ${result.difficulty}, '${result.chainwork}',
			'${result.previousblockhash == undefined ? "" : result.previousblockhash}', 
            '${result.nextblockhash == undefined ? "" : result.nextblockhash}');`;
    global.gc();
    return sql;
}

// getBitTransanctions by address
let getBitTransanctions = async (transactionHash) => {
    try {
        // getBitTransanctions by address
        return await pg.any(`SELECT * FROM bittransactions WHERE bittransactions."txid" = '${transactionHash}'`);
        // success

    } catch (error) {
        logger.error("getBitTransanctions error===>", error);
    }
}

module.exports = { getBitTransanctions }


